using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.Contracts;


namespace FoundationLib.Ifc 
{
	/// <summary>
	/// Represents an abstract base for bootstrappers. Bootstrappers are created by inheriting from 
	/// this base class, implementing at least all abstract members, and providing an implementation of 
	/// the IBootstrapperFactory interface which is in charge of instantiating the bootstrapper singleton instance.
	/// </summary>
	public abstract class Bootstrapper : IDisposable 
	{
		/// <summary>
		/// The container for all factory instances ever created in this bootstrapper
		/// </summary>
		private Dictionary<Type, object> factories;
		/// <summary>
		/// The single bootstrapper instance
		/// </summary>
		private static Bootstrapper instance;


		/// <summary>
		/// Creates a new bootstrapper instance
		/// </summary>
		protected Bootstrapper()
		{
			this.factories = new Dictionary<Type, object>();
		}

		/// <summary>
		/// Creates and returns the factory of the given type
		/// </summary>
		/// <returns>The factory of the given type</returns>
		public T Factory<T>() where T : class
		{
			Type type = typeof(T);
			object factory = null;
			if (!this.factories.TryGetValue(type, out factory))
			{
				factory = this.ResolveFactory<T>();
				if (factory != null)
					this.factories.Add(type, factory);
			}
			return factory as T;
		}

		/// <summary>
		/// Resolves a typed factory of the given kind
		/// </summary>
		/// <returns>The resolved factory instance</returns>
		protected abstract T ResolveFactory<T>() where T : class;

		/// <summary>
		/// Sets up and configures the bootstrapper. This method is called right after the
		/// sigleton bootrapper instance was created.
		/// </summary>
		protected abstract void SetUp();

		/// <summary>
		/// Disposes of the bootstrapper and releases all managed and unmanaged resources
		/// </summary>
		public abstract void Dispose();

		/// <summary>
		/// Creates the only instance of the bootstrapper. This method should be called
		/// once and only once, passing an instantiated factory instance.
		/// </summary>
		/// <param name="factory">The factory creating the singleton bootstrapper
		/// instance</param>
		public static void CreateInstance(IBootstrapperFactory factory)
		{
			if (factory == null)
				throw new ArgumentNullException("factory");
			if (instance == null)
				instance = factory.Create();
		}

		/// <summary>
		/// Gets the current and only instance of the bootstrapper
		/// </summary>
		public static Bootstrapper Current
		{
			get
			{
				if (instance == null)
					throw new InvalidOperationException("The bootstrapper singleton was not created and cannot be accessed.");
				return instance;
			}
		}

	}

}